import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.util.Base64;
import java.util.UUID;

import javax.crypto.Cipher;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import tiptop.util.Md5Utils;

public class RSAUtil {
	/**
	 * 字符串进行加密算法的名称
	 */
	public static final String ALGORITHM = "RSA";
	/**
	 * 字符串进行加密填充的名称
	 */
	public static final String PADDING = "RSA/NONE/NoPadding";
	/**
	 * 字符串持有安全提供者的名称
	 */
	public static final String PROVIDER = "BC";
	/**
	 * 私钥文件路径(RSAUtil是RSA工具类的类名)
	 */
	public static final String PRIVATE_KEY_FILE = RSAUtil.class.getClassLoader().getResource("").getPath() + "key" + "private_response_key_1.key";
	/**
	 * 公钥文件路径
	 */
	public static final String PUBLIC_KEY_FILE = RSAUtil.class.getClassLoader().getResource("").getPath() + "key" + "public_request_key_1.key";
	
	/**
	 * RSA复杂度,和byte[] 是 KEYSIZE/1024
	 */
	public static final int KEYSIZE = 1024;
	
	/**
	* 测试加密解密
	*/
	public void rsaTest(String str) {
	        System.out.println("[要加密解密的参数:"+str+"]");
	        try {
	            String cipherText = encrypt(str);
	            String plainText = decrypt(cipherText);
	            System.out.println("[加密后的参数为:"+cipherText+"]");
	            System.out.println("[解密后的参数为:"+plainText+"]");
	        } catch (Exception e) {
	        	e.printStackTrace();
	        }
	}
	/**
	 * 将字符串进行RSA加密
	 *
	 * @param text
	 * @return
	 */
	public static String encrypt(String text) {
	    String cipherTextBase64 = "";
	    try {
	    	@SuppressWarnings("resource")
	        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(PUBLIC_KEY_FILE));
	        PublicKey publicKey = (PublicKey) inputStream.readObject();
	        Security.addProvider(new BouncyCastleProvider());
	        Cipher cipher = Cipher.getInstance(PADDING, PROVIDER);
	        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
	        byte[] cipherText = cipher.doFinal(text.getBytes());
	        cipherTextBase64 = Base64.getEncoder().encodeToString(cipherText);
	    } catch (Exception e) {
	        e.printStackTrace();
	    }
	    return cipherTextBase64;
	}
	
	/**
	 * 将字符串进行RSA解密
	 *
	 * @param str
	 * @return
	 */
	public static String decrypt(String str) {
	    byte[] dectyptedText = null;
	    try {
	        @SuppressWarnings("resource")
			ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(PRIVATE_KEY_FILE));
	        PrivateKey privateKey = (PrivateKey) inputStream.readObject();
	        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
	        Cipher cipher = Cipher.getInstance(PADDING, PROVIDER);
	        cipher.init(Cipher.DECRYPT_MODE, privateKey);
	        byte[] text = Base64.getDecoder().decode(str);
	        dectyptedText = cipher.doFinal(text);
	    } catch (Exception e) {
	        e.printStackTrace();
	    }
	    return new String(dectyptedText);
	}

	/**
	 * 判断秘钥文件是否存在
	 *
	 * @return
	 */
	public static boolean areKeysPresent() {
	    File privateKey = new File(PRIVATE_KEY_FILE);
	    File publicKey = new File(PUBLIC_KEY_FILE);
	    if (privateKey.exists() && publicKey.exists()) {
	        return true;
	    }
	    return false;
	}

	/**
	 * 生成公钥文件和私钥文件
	 */
	public static boolean generateKey() {
	    try {
	        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
	        final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER);
	        keyGen.initialize(KEYSIZE);
	        final KeyPair key = keyGen.generateKeyPair();
	        File privateKeyFile = new File(PRIVATE_KEY_FILE);
	        File publicKeyFile = new File(PUBLIC_KEY_FILE);
	        if (privateKeyFile.getParentFile() != null) {
	            privateKeyFile.getParentFile().mkdirs();
	        }
	        privateKeyFile.createNewFile();
	        if (publicKeyFile.getParentFile() != null) {
	            publicKeyFile.getParentFile().mkdirs();
	        }
	        publicKeyFile.createNewFile();
	        System.out.println(publicKeyFile);
	        ObjectOutputStream publicKeyOS = new ObjectOutputStream(new FileOutputStream(publicKeyFile));
	        publicKeyOS.writeObject(key.getPublic());
	        publicKeyOS.close();
	        ObjectOutputStream privateKeyOS = new ObjectOutputStream(new FileOutputStream(privateKeyFile));
	        privateKeyOS.writeObject(key.getPrivate());
	        privateKeyOS.close();
	        return true;
	    } catch (Exception e) {
	        e.printStackTrace();
	        return false;
	    }
	}
	
	/**
	 * 文件文末追加内容
	 * @param filePath
	 * @param content
	 */
	public static boolean appendMethodFile(String filePath, String content) {
		try {
			// 打开一个随机访问文件流，按读写方式
			RandomAccessFile randomFile = new RandomAccessFile(filePath, "rw");
			// 文件长度，字节数
			long fileLength = randomFile.length();
			// 将写文件指针移到文件尾。
			randomFile.seek(fileLength);
			randomFile.writeBytes(content);
			randomFile.close();
			return true;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
	}
	
	/**
	 * 文件加密
	 * @param filePath 文件
	 * @param keyLength 校验码长度
	 * @return
	 */
	public static boolean encryptFile(String filePath) {
	    try {
	    	//检测需要加密文件
	    	File file = new File(filePath);
			String path = file.getPath();
			if (!file.exists()) {
				return false;
			}
			
			//生成文件校验信息
			String fileMD5 = Md5Utils.fileMD5(filePath);
			
			//初始化加密信息
			@SuppressWarnings("resource")
	        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(PUBLIC_KEY_FILE));
	        PublicKey publicKey = (PublicKey) inputStream.readObject();
	        Security.addProvider(new BouncyCastleProvider());
	        Cipher cipher = Cipher.getInstance(PADDING, PROVIDER);
	        cipher.init(Cipher.DECRYPT_MODE, publicKey);
	        
	        //初始化文件
			int index = path.lastIndexOf("\\");
			String destFile = path.substring(0, index) + File.separatorChar + UUID.randomUUID().toString();
			File dest = new File(destFile);
			InputStream in = new FileInputStream(filePath);
			OutputStream out = new FileOutputStream(destFile);
			byte[] buffer = new byte[KEYSIZE/8];
			int r;
			byte[] buffer2 = new byte[KEYSIZE/8];
			while ((r = in.read(buffer)) > 0) {
				System.out.println(r);
				if(r-KEYSIZE/8>0) {
					buffer2 = cipher.doFinal(buffer, 0,r-1);
				}else {
					buffer2 = cipher.doFinal(buffer, 0,r-1);
				}
				out.write(buffer2, 0, buffer2.length);
				out.flush();
			}
			in.close();
			out.close();
			file.delete();
			dest.renameTo(new File(filePath));
			
			//校验信息追加
			//appendMethodFile(filePath,fileMD5);
			return true;
	    } catch (Exception e) {
	        e.printStackTrace();
	        return false;
	    }
	}
	
	/**
	 * 解密文件
	 * @param filePath
	 * @return
	 */
	public static boolean decryptFile(String filePath) {
	    try {
	    	//检测需要解密文件
	    	File file = new File(filePath);
	    	String path = file.getPath();
			if (!file.exists()) {
				return false;
			}
			//初始化解密信息
			@SuppressWarnings("resource")
			ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(PRIVATE_KEY_FILE));
		    PrivateKey privateKey = (PrivateKey) inputStream.readObject();
		    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
		    Cipher cipher = Cipher.getInstance(PADDING, PROVIDER);
		    cipher.init(Cipher.DECRYPT_MODE, privateKey);
			
			//初始化文件
			int index = path.lastIndexOf("\\");
			String destFile = path.substring(0, index) + File.separatorChar + UUID.randomUUID().toString();
			File dest = new File(destFile);
			if (!dest.getParentFile().exists()) {
				dest.getParentFile().mkdirs();
			}

			InputStream in = new FileInputStream(filePath);
			OutputStream out = new FileOutputStream(destFile);
			byte[] buffer = new byte[KEYSIZE/8];
			byte[] buffer2 = new byte[KEYSIZE/8];
			int r;
			//分段加密文件
			while ((r = in.read(buffer)) > 0) {
				if(r-KEYSIZE/8>0) {
					buffer2 = cipher.doFinal(buffer, 0,r-1);
				}else{
					buffer2 = cipher.doFinal(buffer, 0, r);
				}
				out.write(buffer2, 0, buffer2.length);
				out.flush();
			}
			in.close();
			out.close();
			file.delete();
			dest.renameTo(new File(filePath));
			return true;
	    } catch (Exception e) {
	        e.printStackTrace();
	        return false;
	    }
	}
	
	public static void main(String[] args) {
		//generateKey();
		System.out.println(encryptFile("D:\\DocAudit.xlsx"));
		//System.out.println(decryptFile("D:\\DocAudit.xlsx"));
	}
}
